<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>实现computed与依赖收集</title>
		<!-- <script src="js/vue.js" type="text/javascript" charset="utf-8"></script> -->
	</head>
	<body>
	<div id="app">
	
	</div>
		<script type="text/javascript">
		
			/**
			 * {
				 value:函数执行返回结果
				 get:get
				 dep:[a,b]依赖参数，监控dep改变，触发get函数。
				}
			 * */
			var computedData={};
			var reg_var=/\{\{(.+?)\}\}/
			var dataPool={};
			function Vue(options){
				this.$el = document.querySelector(options.el);
				this.$data=options.data();
				console.log(this)
				this._init(this,options.computed,options.template);
			}
			Vue.prototype._init = function(vm,computed,template){
				dataReactive(vm);
				computedReactive(vm,computed);
				render(vm,template);
			}
		
			function render(vm,template){
				var container= document.createElement('div');
				_el=vm.$el;
				container.innerHTML=template;
				var domTree=_compileTemplate(vm,container);
				_el.appendChild(domTree);
			}
			function update(vm,key){
				dataPool[key].textContent=vm[key];
			}
			function _compileTemplate(vm,container){
				var allNodes=container.getElementsByTagName('*'),
				nodeItem=null;
				for(var i = 0;i<allNodes.length;i++){
					nodeItem=allNodes[i];
					var matched=nodeItem.textContent.match(reg_var);
					if(matched){
						nodeItem.textContent=nodeItem.textContent.replace(reg_var,function(node,key){
								dataPool[key.trim()]=nodeItem;
								return vm[key.trim()]
						})
					}
				}
				return container
			}
			function dataReactive(vm){
				var _data=vm.$data;
				
				for(var key in _data){
					(function (key){
						Object.defineProperty(vm,key,{
							get(){
								return _data[key];
							},
							set(newValue){
								_data[key]=newValue;
								update(vm,key);
								_updateComputedData(vm,key,function(key){
									update(vm,key);
								})
							}
						})
					})(key)
				}
			}
			function computedReactive(vm,computed){
				 _initComputedData(vm,computed);
				 for(var key in computedData){
					 (function (key){
						 Object.defineProperty(vm,key,{
							 get(){
								 return computedData[key].value;
							 },
							 set(newValue){
								 computedData[key].value=newValue;
							 }
						 })
					 })(key)
				 }
			}
			function _initComputedData(vm,computed){
				for(var key in computed){
					var descriptor=Object.getOwnPropertyDescriptor(computed,key),
						descriptorFn = descriptor.value.get
										?descriptor.value.get
										:descriptor.value;
						
						computedData[key] = {};
						computedData[key].value=descriptorFn.call(vm);
						computedData[key].get=descriptorFn.bind(vm);
						computedData[key].dep=_collectDep(descriptorFn);
				}
			}
			function _collectDep(fn){
				var _collection =fn.toString().match(/this.(.+?)/g);
				if(_collection.length>0){
					for(var i = 0;i<_collection.length;i++){
						_collection[i]=_collection[i].split('.')[1];
					}
				}
			}
			
			function _updateComputedData(vm,key,update){
				var _dep=null;
				for(var _key in computedData){
					_dep=computedData[_key].dep;
					for(var i=0;i<_dep.length;i++){
						if(_dep[i] === key){
							vm[_key] = computedData[_key].get();
							update(_key);
						}
					}
				}
			}
			var vm = new Vue({
				el:'#app',
				data(){
					return{
						a:1,
						b:2
					}
				},
				computed:{
					total(){
						console.log('total')
						return this.a+this.b;
					}
				},
				template:
				`
					<span id="">{{a}}</span>
					<span>+</span>
					<span>{{b}}</span>
					<span>=</span>
					<span>{{total}}</span>
				`
			})
			console.log(vm);
			console.log(vm.total);
			console.log(vm.total);
			console.log(vm.total);
			console.log(vm.total);
			console.log(vm.total);

		</script>
	</body>
</html>
